package com.summer.boss.auth;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Lists;
import com.summer.boss.enums.ResponseCodeEnum;
import com.summer.boss.model.ResponseResult;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import javax.annotation.Resource;

/**
 * @author john
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
    @Resource
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;


    /**
     * jwt认证 + @PreAuthorize("@permissionService.check(permissionCode)")鉴权
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
        //使用自己的前置拦截器(如果前置拦截器把认证和鉴权的事做了,感觉就没必要用SpringSecurity了,所以这里只做认证,把鉴权交给SpringSecurity)
        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
            // 定制我们自己的 session 策略：调整为让 Spring Security 不创建和使用 session
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)

            .and().authorizeRequests()
            //swagger接口文档支持公开访问
            .antMatchers("/favicon.ico", "/static/**", "/public/**", "/resources/**", "/v2/api-docs",
                    "/doc.html", "/swagger-resources/**", "/configuration/**", "/webjars/**").permitAll()
            // 登录接口
            .antMatchers("/boss/login").permitAll()
            // 测试接口可任意访问
            .antMatchers("/test/**").permitAll()
            .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
            ///其他请求都需要认证,jwt认证通过创建安全上下文
            .anyRequest().authenticated()
            .and().exceptionHandling().authenticationEntryPoint(authenticationEntryPoint())
            //解决跨域
            .and().cors()
            // 关闭csrf防护
            .and().csrf().disable()
                // 默认登录页面'/login' 默认用户名/密码字段 username/password Spring Security 会自动生成端点(登录接口)，不方便扩展登录逻辑
                .formLogin().disable();
    }

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(12);
    }

    @Bean
    public AuthenticationEntryPoint authenticationEntryPoint() {
        return (request, response, authException) -> response.getWriter().write(new ObjectMapper().writeValueAsString(ResponseResult.failure(ResponseCodeEnum.TOKEN_ERROR)));
    }

    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();

        // 允许的源（* 表示允许所有，生产环境建议指定具体域名）
        config.setAllowedOriginPatterns(Lists.newArrayList("*"));
        // 允许的请求方法（GET, POST, PUT, DELETE, OPTIONS 等）
        config.addAllowedMethod("*");
        // 允许的请求头（Authorization, Content-Type 等）
        config.addAllowedHeader("*");
        // 是否允许发送 Cookie（如果前端需要携带 Cookie，需设置为 true）
        config.setAllowCredentials(true);
        // 预检请求（OPTIONS）的缓存时间（单位：秒）
        config.setMaxAge(3600L);

        // 对所有路径生效
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }
}
